Skip to content

Library support for Cytron NanoXRP board#94

Open
SaintSampo wants to merge 23 commits intomainfrom
jacob-nanoxrp
Open

Library support for Cytron NanoXRP board#94
SaintSampo wants to merge 23 commits intomainfrom
jacob-nanoxrp

Conversation

@SaintSampo
Copy link
Copy Markdown
Collaborator

@SaintSampo SaintSampo commented Apr 6, 2026

Library support for Cytron NanoXRP board including:

  • Supporting buzzer hardware
  • removed 4th motor
  • Drivetrain dimension differences and alternative PID
  • Accounting for different IMU orientation
  • Added 3rd reflectance sensor

SaintSampo and others added 23 commits March 14, 2025 14:39
Updated defaults.py, encoded_motor.py, and resetbot.py to only initialize and use the fourth motor if Pin.board.MOTOR_4_IN_1 is available. This improves compatibility with hardware configurations that do not include a fourth motor.
Import sys.implementation and make defaults platform-aware: instantiate motor_four conditionally, remove duplicate instantiation, and choose a NanoXRP-specific drivetrain with explicit wheel dimensions. Replace sys.implementation usage in EncodedMotor with a direct import. Extend Encoder to accept a flip_dir flag, normalize pin ordering at init, and invert returned counts when flip_dir is true to allow reversing encoder direction.
Adjust defaults and encoder behavior for NanoXRP hardware. In defaults.py the drivetrain is constructed directly for NanoXRP with left_motor, right_motor, imu and corrected wheel_diam (3.46) and wheel_track (7.8) instead of the invalid annotated call. In encoder.py the module now imports sys.implementation and selects a NanoXRP-specific gear_ratio (68) while keeping counts_per_motor_shaft_revolution at 12; other platforms retain the original gear ratio. These changes adapt drivetrain and encoder resolution to the NanoXRP platform.
add a runtime guard for the middle reflectance sensor to avoid crashes on older XRP boards where LINE_M (middle pin) isn't defined. Docstrings updated to reflect the widened pin type.
Ensure the encoder's base and next pins are determined by numeric GPIO ID instead of comparing Pin string representations. Introduce local pA/pB pins and a helper _get_pin_id that extracts the numeric GPIO from machine.Pin (with a regex fallback to id()). Update docstring types for encAPin/encBPin to allow int/str. This makes the in_base selection for the RP2 StateMachine more reliable across MicroPython ports.
motor, encoder, and sensor support for NanoXRP hardware
Detect NanoXRP via sys.implementation and apply platform-specific tweaks: import implementation in motor/imu/differential_drive, skip creating heading PID on NanoXRP, and use different PID gains for main/secondary controllers for distance/rotation on NanoXRP versus other platforms. Also flip IMU yaw sign on NanoXRP and invert motor flip_dir handling to correct orientation differences. These changes adapt control and sensor behavior to NanoXRP's hardware quirks and dynamics.
Introduce XRPLib/buzzer.py: a Buzzer class for XRP robots that plays notes and songs (blocking and non-blocking). Features note parsing (naturals, sharps, flats), tempo/duration conversion, frequency computation, PWM control on default pin 13, a Timer-based non-blocking playback path, and several built-in songs. Also update XRPLib/defaults.py to import Buzzer and expose a default buzzer instance (buzzer) when running on NanoXRP via Buzzer.get_default_buzzer().
Move gyro delta calculations into the NanoXRP branch and correctly swap pitch/roll while inverting yaw. Previously the deltas were computed before the platform check and only yaw was negated, which produced incorrect pitch/roll mapping on NanoXRP devices. The original computation is preserved for other implementations.
Add XRPExamples/buzzer_examples.py demonstrating basic note playback, custom songs, background playback with movement, and driving+beeping examples.

Refactor XRPLib/buzzer.py: remove unused urandom import and the fixed BUZZER_PIN constant; change constructor to accept buzzer_pin (int|str) and prefer a BOARD_BUZZER entry on machine.Pin.board when no pin is provided, otherwise raise a clear exception. Initialize PWM duty to 0 on creation.

Update XRPLib/defaults.py to detect a board buzzer using hasattr(Pin.board, "BOARD_BUZZER") before creating the buzzer instance. These changes make buzzer configuration more flexible and explicit for boards with a predefined buzzer pin.
Add a reset_buzzer method to Buzzer to immediately stop PWM output, deinit timers, and clear song state so the buzzer is silenced and reset. Shorten the missing-pin exception message. Add a reset_buzzer helper in resetbot.py and wire it into reset_hard and module-level cleanup (with try/except guards) so the buzzer is turned off during resets.
Add a runtime check in Reflectance.get_middle that raises RuntimeError when the middle reflectance pin is not supported on the board. Update the docstring to document the raised error and clarify the return value (reflectance 0..1). This prevents calling _get_value with a None sensor and gives a clearer failure mode for unsupported hardware configurations.
Implement proper inter-note silence and robust non-blocking playback for the Buzzer.

- Add _in_gap and _gap_end_time state to track short gaps between notes instead of sleeping inside the timer callback; refactor _update to advance songs after the gap expires and stop the timer when the song finishes.
- Fix note parsing by initializing octave_pos and handling modifier positions to avoid undefined behavior.
- When starting single non-blocking notes, clear any active song state and reset gap flags; when starting a non-blocking song, stop any existing timer/playback before beginning.
- Ensure _in_gap is cleared on reset.
- Update resetbot to call Buzzer.get_default_buzzer().reset_buzzer() (use instance) instead of calling a class method.

These changes remove blocking sleeps from timer callbacks, prevent race conditions between single-note and song playback, and make non-blocking behavior deterministic.
Move the re import to the module top in XRPLib/encoder.py and remove the redundant local import. In XRPLib/motor.py, replace the expression flip_dir ^ True with the clearer and correct not flip_dir for NanoXRP, ensuring proper boolean inversion and improving readability.
 Clean up example and library code: remove unused Buzzer import/instantiation from examples, restore trailing newline. In XRPLib.buzzer remove duration constants. In differential_drive always initialize heading_pid when an IMU exists and fix comment/indentation around secondary controller setup. In IMU cache the NanoXRP check in self._is_nanoxrp and use it where needed. In reflectance replace an f-string with a plain string in a RuntimeError for minor linting consistency.
Clean up example and library code: remove unused Buzzer import/instantiation from examples, restore trailing newline. In XRPLib.buzzer remove duration constants. In differential_drive always initialize heading_pid when an IMU exists and fix comment/indentation around secondary controller setup. In IMU cache the NanoXRP check in self._is_nanoxrp and use it where needed. In reflectance replace an f-string with a plain string in a RuntimeError for minor linting consistency.

Non-blocking buzzer inter-note gap handling

Implement proper inter-note silence and robust non-blocking playback for the Buzzer.

- Add _in_gap and _gap_end_time state to track short gaps between notes instead of sleeping inside the timer callback; refactor _update to advance songs after the gap expires and stop the timer when the song finishes.
- Fix note parsing by initializing octave_pos and handling modifier positions to avoid undefined behavior.
- When starting single non-blocking notes, clear any active song state and reset gap flags; when starting a non-blocking song, stop any existing timer/playback before beginning.
- Ensure _in_gap is cleared on reset.
- Update resetbot to call Buzzer.get_default_buzzer().reset_buzzer() (use instance) instead of calling a class method.

These changes remove blocking sleeps from timer callbacks, prevent race conditions between single-note and song playback, and make non-blocking behavior deterministic.

Clean up imports and fix flip_dir logic

Move the re import to the module top in XRPLib/encoder.py and remove the redundant local import. In XRPLib/motor.py, replace the expression flip_dir ^ True with the clearer and correct not flip_dir for NanoXRP, ensuring proper boolean inversion and improving readability.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant